package org.stephen.recommend.recommender;

import org.stephen.recommend.enums.LanguageType;
import org.stephen.recommend.algorithms.AbstractRecommender;
import org.stephen.recommend.model.HotNewsList;
import org.stephen.recommend.service.CmsArticleService;
import org.stephen.recommend.thread.AsyncService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.*;

/**
 * ClassName: HotRecommender
 * Description: 基于“热点新闻”生成的推荐，一般用于在 CF 和 CB 算法推荐结果数较少时进行数目的补充
 * Date: 2020/12/8 20:37
 *
 * @author stephen.qiu
 * @version 1.0
 */
@Component
@DependsOn("redisTemplate")
public class HotRecommender extends AbstractRecommender {
    private static final Logger logger = LoggerFactory.getLogger(HotRecommender.class);

    @Autowired
    CmsArticleService cmsArticleService;

    @Autowired
    AsyncService asyncService;

    /**
     * Describe:
     * 初始化热度推荐文章，
     * 每隔一段时间再次初始化，时间间隔:propGetKit.getUpdateMin()，
     * 排除几填之前的文章，天数:propGetKit.getBeforeDays()
     */
    @PostConstruct
    public void initHotNewsList() {
        if (HotNewsList.getInstance().getArticleList() == null || HotNewsList.getInstance().getLastInitTopHotNewsListTime().isBefore(LocalDateTime.now().plusMinutes(0 - propGetKit.getUpdateMin()))) {
            asyncService.initHotNewsList(propGetKit.getBeforeDays(), LanguageType.EN_US);
            asyncService.initHotNewsList(propGetKit.getBeforeDays(), LanguageType.ZH_CN);
        }
    }

    public List<String> getHotNewsListWithOutBlockArticleIds(Set<String> blockArticleIds, int num, LanguageType languageType) {
        initHotNewsList();
        List<String> hotRecommendArticleIds = new LinkedList<>();
        for (String id : HotNewsList.getInstance().getArticleIdsList(languageType)) {
            if (blockArticleIds == null || !blockArticleIds.contains(id)) {
                hotRecommendArticleIds.add(id);
                if (hotRecommendArticleIds.size() == num) {
                    return hotRecommendArticleIds;
                }
            }
        }
        return hotRecommendArticleIds;
    }

    /**
     * 针对特定用户返回推荐结果
     * Description:
     * 获取基于协同过滤和基于内容所推荐的文章，若不足推荐数量，则使用热点新闻补充,
     * 从头开始筛选num篇文章，如果已经推荐过，则顺位到下一篇。
     *
     * @param blockArticleIds 用户屏蔽的文章
     * @param userId          用户id/设备id
     * @param languageType    语言环境
     */
    @Override
    public void recommend(List<String> blockArticleIds, String userId, LanguageType languageType) {
        logger.info("----------开始基于热度推荐----------");
        /**
         * 获取基于协同过滤和基于内容所推荐的文章，若不足推荐数量，则使用热点新闻补充。
         */
        String recommendArticleIds = getRecommendArticleIds(userId);
        if (recommendArticleIds == null || recommendArticleIds.split(",").length < propGetKit.getTotalRecNum()) {
            /**
             * 获取所有不会被推荐的文章列表
             */
            Set<String> allBlockArticleIdsSet = getAllBlockArticleIdsSet(userId, blockArticleIds);
            /**
             * 搜索热点新闻推荐的时候去除已经推荐的文章和马上即将推荐的文章和用户屏蔽文章和用户浏览过的文章
             */
            int hotRecommendNum = propGetKit.getTotalRecNum() - (recommendArticleIds == null ? 0 : recommendArticleIds.split(",").length);
            List<String> hotRecommendArticles = getHotNewsListWithOutBlockArticleIds(allBlockArticleIdsSet, hotRecommendNum, languageType);

            logger.info("HOT热度推荐文章数量:" + hotRecommendArticles.size());

            setRecommendArticles(hotRecommendArticles, userId);
        } else {
            logger.info("HOT热度推荐文章数量:" + 0);
        }
    }
}
